package cn.wangkai.peanut.db.util;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.lang.StringUtils;

import cn.wangkai.peanut.db.mod.Comparison;

public class BeanUtil {
	
	/**
	 * 
	 */
	protected static final String PROPERTY_NOT_FOUND = null;

	/**
	 * 
	 * @param rsh
	 * @param type
	 * @return
	 * @throws SQLException
	 */
	public static  Object MaptoBean(HashMap<String,Object> rsh, Class<?> type) throws SQLException{
		BeanUtil beanUtil = new BeanUtil();
		return beanUtil.toBean(rsh, type);
	}
	
	/**
	 * 
	 * @param rsh
	 * @param type
	 * @return
	 * @throws SQLException
	 */
	public Object toBean(HashMap<String,Object> rsh, Class<?> type) throws SQLException {
		PropertyDescriptor[] props = this.propertyDescriptors(type);
		String[] columnToProperty = this.mapColumnsToProperties(rsh, props);
		return this.createBean(rsh, type, props, columnToProperty);
	}

	/**
	 * 
	 * @param c
	 * @return
	 * @throws SQLException
	 */
	protected Object newInstance(Class<?> c) throws SQLException {
		try {
			return c.newInstance();

		} catch (InstantiationException e) {
			throw new SQLException("Cannot create " + c.getName() + ": "
					+ e.getMessage());

		} catch (IllegalAccessException e) {
			throw new SQLException("Cannot create " + c.getName() + ": "
					+ e.getMessage());
		}
	}

	/**
	 * 
	 * @param rsh
	 * @param type
	 * @param props
	 * @param columnToProperty
	 * @return
	 * @throws SQLException
	 */
	private Object createBean(HashMap<String,Object> rsh, Class<?> type,
			PropertyDescriptor[] props, String[] columnToProperty)
			throws SQLException {

		Object bean = this.newInstance(type);

		for (int i = 0; i < columnToProperty.length; i++) {
			if (columnToProperty[i] == PROPERTY_NOT_FOUND) {
				continue;
			}
			Object value = this.processColumn(rsh, columnToProperty[i]);
			this.callSetter(bean, columnToProperty[i], value);
		}

		return bean;
	}

	/**
	 * 
	 * @param target
	 * @param prop
	 * @param value
	 * @throws SQLException
	 */
	private void callSetter(Object target, String prop, Object value)
			throws SQLException {
		Method method;
		try {
			method = getClassMethod(target, prop);
			method.invoke(target, new Object[] { value });
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}


	}
	
	/**
	 * 
	 * @param obj
	 * @param filed
	 * @return
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 */
	private static Method getClassMethod(Object obj,String filed) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException{
		Method[] methods = obj.getClass().getMethods();
		for (int i = 0; i < methods.length; i++) {
			Method method = methods[i];
			if(StringUtils.equalsIgnoreCase("set"+filed,method.getName())){
				return method;
			}
		}
		return null;
	}
	
	/**
	 * 
	 * @param c
	 * @return
	 * @throws SQLException
	 */
	private PropertyDescriptor[] propertyDescriptors(Class<?> c)
			throws SQLException {
		BeanInfo beanInfo = null;
		try {
			beanInfo = Introspector.getBeanInfo(c);
		} catch (IntrospectionException e) {
			throw new SQLException("Bean introspection failed: "
					+ e.getMessage());
		}

		return beanInfo.getPropertyDescriptors();
	}

	/**
	 * 
	 * @param rsh
	 * @param props
	 * @return
	 * @throws SQLException
	 */
	protected String[] mapColumnsToProperties(HashMap<String,Object> rsh,
			PropertyDescriptor[] props) throws SQLException {
		Set<String> rsmd = rsh.keySet();
		int cols = rsmd.size();
		String columnToProperty[] = new String[cols + 1];
		Arrays.fill(columnToProperty, PROPERTY_NOT_FOUND);
		int j = 0;
		for (Iterator<String> iterator = rsmd.iterator(); iterator.hasNext();) {
			String columnName = (String) iterator.next();
			for (int i = 0; i < props.length; i++) {
				if (columnName.equalsIgnoreCase(props[i].getName())) {
					columnToProperty[j] = columnName;
					break;
				}
			}
			j++;
		}

		return columnToProperty;
	}

	/**
	 * 
	 * @param rsh
	 * @param keyname
	 * @return
	 */
	protected Object getMapObject(HashMap<String,Object> rsh, String keyname) {
		Object objs = null;
		for (Iterator<Entry<String, Object>> it = rsh.entrySet().iterator(); it.hasNext();) {
			Entry<String, Object> entry = (Entry<String, Object>) it.next();
			if (StringUtils.equalsIgnoreCase(String.valueOf(entry.getKey()),keyname)) {
				objs = entry.getValue();
				break;
			}
		}
		return objs;
	}

	/**
	 * 
	 * @param rsh
	 * @param columnToProperty
	 * @return
	 * @throws SQLException
	 */
	protected Object processColumn(HashMap<String,Object> rsh, String columnToProperty)
			throws SQLException {
		Object obj = getMapObject(rsh, columnToProperty);
		return obj;
	}
	
	/**
	 * 比较两个对象差异数据
	 * @param beforeobj
	 * @param afterobj
	 * @param cols
	 * @return
	 * @throws Exception 
	 */
	public static List<Comparison> compareobj(Object beforeobj,Object afterobj,Map<String, String> cols,boolean different) throws Exception{
		List<Comparison> sames = new ArrayList<Comparison>();
		List<Comparison> differents = new ArrayList<Comparison>();
		if(beforeobj==null) throw new Exception("beforeobj不能为空!");
		if(afterobj==null) throw new Exception("afterobj不能为空!");
		if(!beforeobj.getClass().isAssignableFrom(afterobj.getClass())){
			throw new Exception("两个对象不相同，无法比较");
		}
		//去除对象所有字段
		PropertyDescriptor[] descriptors = Introspector.getBeanInfo(beforeobj.getClass()).getPropertyDescriptors();
		Set<String> keys = cols.keySet();
		for (String col : keys) {
			for (PropertyDescriptor descriptor : descriptors) {
				String proname = descriptor.getName();
				if(StringUtils.isNotEmpty(proname)&&proname.equalsIgnoreCase(col)){
					Comparison comparison = new Comparison();
					Method readmethod = descriptor.getReadMethod();
					comparison.setBefore(readmethod.invoke(beforeobj));
					comparison.setAfter(readmethod.invoke(afterobj));
					comparison.setFieldType(descriptor.getPropertyType());
					comparison.setField(proname);
					comparison.setFieldName(cols.get(col));
					if(comparison.isdifferent()){
						sames.add(comparison);
					}else{
						differents.add(comparison);
					}
				}
			}
		}
		if(different){
			return differents;
		}else{
			return sames;
		}
	}
	
}
